using System;
using System.IO;
using System.Reflection;
using System.Xml;

using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;

namespace CSP.CRM.Deployer
{


  public class PluginDeployer
  {

    private CrmService crm;
    private TextWriter log;
    private string[] procedureNames;
    private string cspPrefix;

    public PluginDeployer(CrmService crm, string cspPrefix, string[] procedureNames, TextWriter log)
    {
      this.crm = crm;
      this.log = log;
      this.procedureNames = procedureNames;
      this.cspPrefix = cspPrefix;
    }

    public void Run()
    {
      log.WriteLine("CSP Plugin Deployer");

      XmlDocument registerdoc = new XmlDocument();

      string filesDirectory = Directory.GetCurrentDirectory() + @"\files\";
      string configFilePath = filesDirectory + @"csp_plugindeployment.xml";
      TrimConfigurationFile(configFilePath);

      registerdoc.Load(configFilePath);      

      XmlNodeList solutionnodes = registerdoc.GetElementsByTagName("Solution");

      foreach (XmlNode solutionnode in solutionnodes)
      {
        Solution solution = new Solution(solutionnode);

        int dupes = solution.DeleteDuplicates(crm);
        if (dupes > 0)
          log.WriteLine(dupes.ToString() + " duplicate Solutions Deleted");

        RegisterSolutionRequest request = new RegisterSolutionRequest();
        request.PluginAssembly = solution.PluginAssembly;
        request.Steps = solution.Steps;
        RegisterSolutionResponse response = (RegisterSolutionResponse)crm.Execute(request);
        log.WriteLine("Assembly registered: " + solution.pluginassembly.name);

        foreach (plugintype plugintype in solution.WorkflowTypes)
        {
          plugintype.pluginassemblyid = new Lookup();
          plugintype.pluginassemblyid.type = EntityName.pluginassembly.ToString();
          plugintype.pluginassemblyid.Value = response.PluginAssemblyId;
          plugintype.isworkflowactivity = new CrmBoolean();
          plugintype.isworkflowactivity.Value = true;
          crm.Create(plugintype);
          log.WriteLine("Workflow registered: " + plugintype.typename);
        }
      }
    }

    private void TrimConfigurationFile(string configFilePath)
    {
      XmlDocument configDoc = new XmlDocument();
      configDoc.Load(configFilePath);
      
      XmlElement registerNode = (XmlElement)configDoc.DocumentElement;
      Uri serverUri = new Uri(crm.Url);
      string serverUrl = String.Format("http://{0}:{1}", serverUri.Host, serverUri.Port);
      string organizationName = crm.CrmAuthenticationTokenValue.OrganizationName;
      registerNode.SetAttribute("Server", serverUrl);
      registerNode.SetAttribute("Org", organizationName);


      XmlNode stepsNode = configDoc.SelectSingleNode("/Register/Solution/PluginTypes/Plugin/Steps");
      foreach (string procedureName in procedureNames)
        AddStepConfiguration(procedureName, stepsNode);
      configDoc.Save(configFilePath);    
    }


    private void AddStepConfiguration(string procedureName, XmlNode stepsNode)
    {
      string stepXml = @"
					<Step CustomConfiguration='' SecureConfiguration='' Description='Create of {0} in Parent Pipeline'
								FilteringAttributes='' ImpersonatingUserId='' InvocationSource='0'
								MessageName='Create' Mode='0'
								PrimaryEntityName='{0}' SecondaryEntityName='none'
								PluginTypeName='CSP.CRM.Plugins.LinkIncidentToProcedure'
								PluginTypeFriendlyName='Link incident to procedure'
								Stage='50' SupportedDeployment='0' Rank='1' Id='{1}'>
						<Images />
					</Step>";
      stepXml = String.Format(stepXml, procedureName, Guid.NewGuid());
      XmlDocumentFragment frag = stepsNode.OwnerDocument.CreateDocumentFragment();
      frag.InnerXml = stepXml;
      stepsNode.AppendChild(frag);
    }
  
  }


  class Solution
  {
    public pluginassembly pluginassembly;
    public SdkMessageProcessingStepRegistration[] steps;
    public plugintype[] workflowtypes;

    public Solution(XmlNode solutionnode)
    {
      pluginassembly = new pluginassembly();
      FileInfo fi = new FileInfo(solutionnode.Attributes["Assembly"].Value);
      string path = fi.FullName;
      Assembly assembly = Assembly.LoadFile(path);
      byte[] bytes = File.ReadAllBytes(path);
      string[] assemblyProp = assembly.GetName().FullName.Split(",= ".ToCharArray(),
          StringSplitOptions.RemoveEmptyEntries);
      pluginassembly.content = Convert.ToBase64String(bytes);
      pluginassembly.culture = assemblyProp[4];
      pluginassembly.name = assemblyProp[0];
      pluginassembly.publickeytoken = assemblyProp[6];
      pluginassembly.sourcetype = new Picklist();
      pluginassembly.sourcetype.Value = Convert.ToInt32(solutionnode.Attributes["SourceType"].Value);
      pluginassembly.version = assemblyProp[2];

      XmlNodeList workflowtypenodes = solutionnode.SelectNodes("WorkflowTypes/WorkflowType");
      workflowtypes = new plugintype[workflowtypenodes.Count];

      if (workflowtypenodes.Count > 0)
      {
        for (int a = 0; a < workflowtypenodes.Count; a++)
        {
          workflowtypes[a] = new plugintype();
          workflowtypes[a].typename = workflowtypenodes[a].Attributes["TypeName"].Value;
          workflowtypes[a].friendlyname = workflowtypenodes[a].Attributes["FriendlyName"].Value;
        }
      }

      XmlNodeList stepnodes = solutionnode.SelectNodes(".//Steps/Step");
      steps = new SdkMessageProcessingStepRegistration[stepnodes.Count];
      if (stepnodes.Count > 0)
      {
        for (int b = 0; b < stepnodes.Count; b++)
        {
          steps[b] = new SdkMessageProcessingStepRegistration();
          steps[b].CustomConfiguration = stepnodes[b].Attributes["CustomConfiguration"].Value;
          steps[b].Description = stepnodes[b].Attributes["Description"].Value;
          steps[b].InvocationSource = Convert.ToInt32(stepnodes[b].Attributes["InvocationSource"].Value);
          steps[b].MessageName = stepnodes[b].Attributes["MessageName"].Value;
          steps[b].Mode = Convert.ToInt32(stepnodes[b].Attributes["Mode"].Value);
          steps[b].PluginTypeFriendlyName = stepnodes[b].Attributes["PluginTypeFriendlyName"].Value;
          steps[b].PluginTypeName = stepnodes[b].Attributes["PluginTypeName"].Value;

          steps[b].Stage = Convert.ToInt32(stepnodes[b].Attributes["Stage"].Value);
          steps[b].SupportedDeployment = Convert.ToInt32(stepnodes[b].Attributes["SupportedDeployment"].Value);

          string FilteringAttributes = stepnodes[b].Attributes["FilteringAttributes"].Value;
          if (FilteringAttributes.Length > 0)
          {
            steps[b].FilteringAttributes = stepnodes[b].Attributes["FilteringAttributes"].Value;
          }

          string ImpersonatingUserId = stepnodes[b].Attributes["ImpersonatingUserId"].Value;
          if (ImpersonatingUserId.Length > 0)
          {
            steps[b].ImpersonatingUserId = new Guid(stepnodes[b].Attributes["ImpersonatingUserId"].Value);
          }

          string PrimaryEntityName = stepnodes[b].Attributes["PrimaryEntityName"].Value;
          if (PrimaryEntityName.Length > 0)
          {
            steps[b].PrimaryEntityName = stepnodes[b].Attributes["PrimaryEntityName"].Value;
          }

          string SecondaryEntityName = stepnodes[b].Attributes["SecondaryEntityName"].Value;
          if (SecondaryEntityName.Length > 0)
          {
            steps[b].SecondaryEntityName = stepnodes[b].Attributes["SecondaryEntityName"].Value;
          }

          XmlNodeList imagenodes = stepnodes[b].SelectNodes("Images/Image");
          if (imagenodes.Count > 0)
          {
            steps[b].Images = new SdkMessageProcessingStepImageRegistration[imagenodes.Count];
            for (int c = 0; c < imagenodes.Count; c++)
            {
              steps[b].Images[c] = new SdkMessageProcessingStepImageRegistration();
              steps[b].Images[c].EntityAlias = imagenodes[c].Attributes["EntityAlias"].Value;
              steps[b].Images[c].ImageType = Convert.ToInt32(imagenodes[c].Attributes["ImageType"].Value);
              steps[b].Images[c].MessagePropertyName = imagenodes[c].Attributes["MessagePropertyName"].Value;

              string AttributeArray = imagenodes[c].Attributes["Attributes"].Value;
              if (AttributeArray != null)
              {
                steps[b].Images[c].Attributes = AttributeArray.Split(',');
              }
            }
          }
        }
      }
    }

    public pluginassembly PluginAssembly
    {
      get { return pluginassembly; }
    }

    public SdkMessageProcessingStepRegistration[] Steps
    {
      get { return steps; }
    }

    public plugintype[] WorkflowTypes
    {
      get { return workflowtypes; }
    }

    public int DeleteDuplicates(CrmService service)
    {
      ColumnSet cols = new ColumnSet();
      cols.AddColumns(new string[] { "pluginassemblyid", "name" });
      
      ConditionExpression cond = new ConditionExpression();
      cond.AttributeName = "name";
      cond.Operator = ConditionOperator.Equal;
      cond.Values = new object[] { pluginassembly.name };
      FilterExpression filter = new FilterExpression();
      filter.FilterOperator = LogicalOperator.And;
      filter.Conditions.Add(cond);
      
      QueryExpression query = new QueryExpression();
      query.ColumnSet = cols;
      query.Criteria = filter;
      query.EntityName = EntityName.pluginassembly.ToString();
      RetrieveMultipleRequest request = new RetrieveMultipleRequest();
      request.Query = query;
      RetrieveMultipleResponse response = (RetrieveMultipleResponse)service.Execute(request);
      int dupes = response.BusinessEntityCollection.BusinessEntities.Count;
      if (dupes > 0)
      {
        for (int i = 0; i < dupes; i++)
        {
          pluginassembly pa = (pluginassembly)response.BusinessEntityCollection.BusinessEntities[i];
          UnregisterSolutionRequest unregister = new UnregisterSolutionRequest();
          unregister.PluginAssemblyId = pa.pluginassemblyid.Value;
          service.Execute(unregister);
        }
      }
      return dupes;
    }
  }





}
